package com.cqupt.utils;

import com.google.gson.Gson;
import com.google.gson.JsonObject;
import okhttp3.*;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.nio.charset.Charset;
import java.text.SimpleDateFormat;
import java.util.*;

public class IseEvaluation extends WebSocketListener {

    private static final String HOST_URL = "https://ise-api.xfyun.cn/v2/open-ise";
    private static final String APP_ID = "a830a3cb"; // 控制台获取
    private static final String API_SECRET = "OWQzNmFkN2U3NmFmMjE5NTAyOTU4MTNl"; // 控制台获取
    private static final String API_KEY = "6a73cfa8e86bde2f546004fcdff19224"; // 控制台获取
    private static final String sub = "ise";
//    private static final String ent = "cn_vip";
    private static final String ent="en_vip";//语言标记参数 ent(cn_vip中文,en_vip英文)
    private static final String category = "read_sentence";
    private String text;
    private InputStream audioStream;
    private String result;

    private final Base64.Encoder encoder = Base64.getEncoder();
    private final Base64.Decoder decoder = Base64.getDecoder();
    private static final Gson json = new Gson();
    private long beginTime;
    private long endTime;

    public IseEvaluation(String text, InputStream audioStream) {
        this.text = text;
        this.audioStream = audioStream;
    }

    public String evaluate() throws Exception {
        String authUrl = getAuthUrl(HOST_URL, API_KEY, API_SECRET);
        OkHttpClient client = new OkHttpClient.Builder().build();
        String url = authUrl.replace("http://", "ws://").replace("https://", "wss://");
        Request request = new Request.Builder().url(url).build();
        WebSocket webSocket = client.newWebSocket(request, this);
        client.dispatcher().executorService().shutdown();

        synchronized (this) {
            wait();
        }

        return result;
    }

    @Override
    public void onOpen(WebSocket webSocket, Response response) {
        new Thread(() -> {
            int frameSize = 1280;
            int intervel = 40;
            int status = 0;

            try (InputStream fs = audioStream) {
                byte[] buffer = new byte[frameSize];
                beginTime = new Date().getTime();

                sendInitialFrame(webSocket);

                while (true) {
                    int len = fs.read(buffer);
                    if (len == -1) {
                        status = 2;
                    }

                    switch (status) {
                        case 0:
                            send(webSocket, 1, 1, Base64.getEncoder().encodeToString(Arrays.copyOf(buffer, len)));
                            status = 1;
                            break;
                        case 1:
                            send(webSocket, 2, 1, Base64.getEncoder().encodeToString(Arrays.copyOf(buffer, len)));
                            break;
                        case 2:
                            send(webSocket, 4, 2, "");
                            endTime = new Date().getTime();
                            break;
                    }
                    if (status == 2) break;
                    Thread.sleep(intervel);
                }

            } catch (Exception e) {
                e.printStackTrace();
            }
        }).start();
    }

    private void sendInitialFrame(WebSocket webSocket) {
        ParamBuilder p = new ParamBuilder();
        p.add("common", new ParamBuilder().add("app_id", APP_ID))
                .add("business", new ParamBuilder()
                        .add("category", category)
                        .add("rstcd", "utf8")

                        .add("ise_unite","1")
                        .add("rst","entirety")
                        .add("extra_ability","multi_dimension")

                        .add("sub", sub)
                        .add("ent", ent)
                        .add("tte", "utf-8")
                        .add("cmd", "ssb")
                        .add("auf", "audio/L16;rate=16000")
                        .add("aue", "lame")
                        .add("text", '\uFEFF' + text))
                .add("data", new ParamBuilder()
                        .add("status", 0)
                        .add("data", ""));
        webSocket.send(p.toString());
    }

    private void send(WebSocket webSocket, int aus, int status, String data) {
        ParamBuilder p = new ParamBuilder();
        p.add("business", new ParamBuilder()
                        .add("cmd", "auw")
                        .add("aus", aus)
                        .add("aue", "lame"))
                .add("data", new ParamBuilder()
                        .add("status", status)
                        .add("data", data)
                        .add("data_type", 1)
                        .add("encoding", "raw"));
        webSocket.send(p.toString());
    }

    @Override
    public void onMessage(WebSocket webSocket, String text) {
        IseNewResponseData resp = json.fromJson(text, IseNewResponseData.class);
        if (resp != null) {
            if (resp.getCode() != 0) {
                System.out.println("code=>" + resp.getCode() + " error=>" + resp.getMessage() + " sid=" + resp.getSid());
                System.out.println("错误码查询链接：https://www.xfyun.cn/document/error-code");
                result = "Error: " + resp.getMessage();
                synchronized (this) {
                    notify();
                }
                return;
            }
            if (resp.getData() != null) {
                if (resp.getData().getStatus() == 2) {
                    try {
                        result = new String(decoder.decode(resp.getData().getData()), "UTF-8");
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    webSocket.close(1000, "");
                    synchronized (this) {
                        notify();
                    }
                }
            }
        }
    }

    private String getAuthUrl(String hostUrl, String apiKey, String apiSecret) throws Exception {
        URL url = new URL(hostUrl);
        SimpleDateFormat format = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss z", Locale.US);
        format.setTimeZone(TimeZone.getTimeZone("GMT"));
        String date = format.format(new Date());

        StringBuilder builder = new StringBuilder("host: ").append(url.getHost()).append("\n")
                .append("date: ").append(date).append("\n")
                .append("GET ").append(url.getPath()).append(" HTTP/1.1");

        Charset charset = Charset.forName("UTF-8");
        Mac mac = Mac.getInstance("hmacsha256");
        SecretKeySpec spec = new SecretKeySpec(apiSecret.getBytes(charset), "hmacsha256");
        mac.init(spec);
        byte[] hexDigits = mac.doFinal(builder.toString().getBytes(charset));
        String sha = encoder.encodeToString(hexDigits);

        String authorization = String.format("api_key=\"%s\", algorithm=\"%s\", headers=\"%s\", signature=\"%s\"",
                apiKey, "hmac-sha256", "host date request-line", sha);

        HttpUrl httpUrl = HttpUrl.parse("https://" + url.getHost() + url.getPath()).newBuilder()
                .addQueryParameter("authorization", encoder.encodeToString(authorization.getBytes(charset)))
                .addQueryParameter("date", date)
                .addQueryParameter("host", url.getHost())
                .build();

        return httpUrl.toString();
    }

    private static class IseNewResponseData {
        private int code;
        private String message;
        private String sid;
        private Data data;

        public int getCode() {
            return code;
        }

        public void setCode(int code) {
            this.code = code;
        }

        public String getMessage() {
            return message;
        }

        public void setMessage(String message) {
            this.message = message;
        }

        public String getSid() {
            return sid;
        }

        public void setSid(String sid) {
            this.sid = sid;
        }

        public Data getData() {
            return data;
        }

        public void setData(Data data) {
            this.data = data;
        }

        public static class Data {
            private int status;
            private String data;

            public int getStatus() {
                return status;
            }

            public void setStatus(int status) {
                this.status = status;
            }

            public String getData() {
                return data;
            }

            public void setData(String data) {
                this.data = data;
            }
        }
    }

    private static class ParamBuilder {
        private final StringBuilder builder = new StringBuilder();
        private boolean isEmpty = true;

        public ParamBuilder add(String key, Object value) {
            if (!isEmpty) {
                builder.append(",");
            }
            builder.append("\"").append(key).append("\":").append(value instanceof String ? "\"" + value + "\"" : value);
            isEmpty = false;
            return this;
        }

        public String toString() {
            return "{" + builder.toString() + "}";
        }
    }
}